"""
主题: 简化数据结构的初始化
问题: 你写了很多仅仅用作数据结构的类，不想写太多烦人的 __init__() 函数
提示 : 
"""

import math

MESSAGE = 'Expected {} arguments'

class Structure1:
    # Class variable that specifies expected fields
    _fields = []

    def __init__(self, *args):
        if len(args) != len(self._fields):
            raise TypeError(MESSAGE.format(len(self._fields)))
        # Set the arguments
        for name, value in zip(self._fields, args):
            setattr(self, name, value)

class Structure2:
    '''支持关键字参数，可以将关键字参数设置为实例属性'''
    _fields = []

    def __init__(self, *args, **kwargs):
        if len(args) > len(self._fields):
            raise TypeError(MESSAGE.format(len(self._fields)))

        # Set all of the positional arguments
        for name, value in zip(self._fields, args):
            setattr(self, name, value)

        # Set the remaining keyword arguments
        for name in self._fields[len(args):]:
            setattr(self, name, kwargs.pop(name))

        # Check for any remaining unknown arguments
        if kwargs:
            raise TypeError('Invalid argument(s): {}'.format(','.join(kwargs)))

class Structure3:
    # Class variable that specifies expected fields
    _fields = []

    def __init__(self, *args, **kwargs):
        if len(args) != len(self._fields):
            raise TypeError(MESSAGE.format(len(self._fields)))

        # Set the arguments
        for name, value in zip(self._fields, args):
            setattr(self, name, value)

        # Set the additional arguments (if any)
        extra_args = kwargs.keys() - self._fields
        for name in extra_args:
            setattr(self, name, kwargs.pop(name))

        if kwargs:
            raise TypeError('Duplicate values for {}'.format(','.join(kwargs)))

def recipe1():
    class Stock(Structure1):
        _fields = ['name', 'shares', 'price']

    class Point(Structure1):
        _fields = ['x', 'y']

    class Circle(Structure1):
        _fields = ['radius']

        def area(self):
            return math.pi * self.radius ** 2

    s = Stock('ACME', 50, 91.1)
    print(f"{vars(s) = }")
    p = Point(2, 3)
    print(f"{vars(p) = }")
    c = Circle(4.5)
    print(f"{vars(c) = }")

    try:
        Point(2)
    except TypeError as e:
        print(e)   

def recipe2():
    class Stock(Structure2):
        _fields = ['name', 'shares', 'price']

    s1 = Stock('ACME', 50, 91.1)
    print(f"{vars(s1) = }")
    s2 = Stock('ACME', 50, price=91.1)
    print(f"{vars(s2) = }")
    s3 = Stock('ACME', shares=50, price=91.1)
    print(f"{vars(s3) = }")

    try:
        Stock('ACME', 50, 91.1, date='8/2/2012')
    except TypeError as e:
        print(e)   

def recipe3():
    class Stock(Structure3):
        _fields = ['name', 'shares', 'price']

    s1 = Stock('ACME', 50, 91.1)
    print(f"{vars(s1) = }")
    s2 = Stock('ACME', 50, 91.1, date='8/2/2012')
    print(f"{vars(s2) = }")

    print(f"{help(Stock) = }")
    

def main():
    print('recipe1'.center(20, '*'))
    recipe1()
    print('recipe2'.center(20, '*'))
    recipe2()
    print('recipe3'.center(20, '*'))
    recipe3()

if __name__ == '__main__':
    main()           

